
window.requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
    return window.setTimeout(callback, 1000 / 30);
};

window.cancelAnimFrame =
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function(callback) {
    return clearTimeout(callback);
};

// 游戏数据存储对象
var gameInfo={

    // 页面上所有的元素
    elements:{},

    // 怪兽元素
    monsters:[],

    // 游戏状态，win,lose,unstart,gaming
    gameState:"",

    // 游戏分数
    score:0,

    // 当前关卡
    currentLevel:0,

    updateCallback:[],

    stopAnimateCallback:""

}

// 游戏配置
var gameConfig={

    canvasPadding: 30, // 默认画布的间隔

    // 子弹
    bulletSize: [1,10], // 默认子弹长度
    bulletSpeed: 10, // 默认子弹的移动速度
    bulletColor: "#fff", // 默认子弹的移动速度

    // 怪兽
    numPerLine: 6, // 游戏默认每行多少个怪兽
    enemySpeed: [2,20], // 默认敌人移动距离
    enemySize: [50,50], // 默认敌人的尺寸
    enemyGap: 10,  // 默认敌人之间的间距
    enemyIcon: '../img/enemy.png', // 怪兽的图像
    enemyBoomIcon: '../img/boom.png', // 怪兽死亡的图像
    enemyDirection: 'right', // 默认敌人一开始往右移动

    // 飞机
    planeSpeed: 5, // 默认飞机每一步移动的距离
    planeSize:[60,100], // 默认飞机的尺寸
    planeIcon: '../img/plane.png',
    
    totalLevel:5,// 所有关卡

}

// 设置个数属性不可遍历，主要为了给页面元素起一个名字
Object.defineProperty(gameInfo.elements,"len",{
    enumerable:false,
    writable:true,
    value:0
})

/***
 *  抽象类
 * 
 */

//  创建节点
class createElement{
    
    constructor({top, left}){

        this.top = top;
        this.left = left;

        var elements = gameInfo.elements

        // 节点数量上涨
        elements.len++

        var ItemName  =this.index = "rect_"+elements.len

        // 保存节点
        elements[ItemName] =this

    }

}

// 基础类
class BaseItem extends createElement{

    constructor({top, left, width, height,speed=10}){

        super({top,left})

        this.width = width;
        this.height = height;
        this.speed = speed;

    }

    // 移动
    move(direction,num){
        

        num = num || this.speed || 10

		switch (direction) {
			case 'up':
				this.top -= num;
				break;

			case 'down':
				this.top += num;
				break;

			case 'left':
				this.left -= num;
				break;

			case 'right':
				this.left += num;
				break;
		}

    }

    // 移动节点
    remove({left,top,w,h}={}){   

        // 移除画布
		context.clearRect(this.left + left, this.top + top, this.width + w, this.height + h);


        // 删除保存的节点
        delete gameInfo.elements[this.index]
    }

    // 判断是否碰撞
    getIsDetect(item){

		var w = this.width;
		var h = this.height;
		var left = this.left;
		var top = this.top;
		var bottom = top + h;
        var right = left + w;

		// 想要检测的物体
		var d_w = item.width;
		var d_h = item.height;
		var d_left = item.left;
		var d_top = item.top;
		var d_right = d_left + d_w;
		var d_bottom = d_top + d_h;
    
		var R = right < d_left, // 右碰到 左
        L = left > d_right, // 左碰到 右
        T = top > d_bottom, // 头碰到了尾
        B = bottom < d_top; //  尾碰到了头

        if(!R&&!L&&!T&&!B){
            return true
        }

        return false

    }

}

// 画图像
class DrawImage extends BaseItem{

    constructor({top, left, width, height,speed,imgSrc}){

        super({top, left, width, height,speed})

        var img = this.img = new Image()
        img.src = imgSrc;

        img.onclick=function (params) {
            console.log(3333333333)
        }
    }

    // 画上去
    draw(context){

        // console.log('this.img',this.img.complete);

        var img = this.img

        if(img.complete){

            context.drawImage(this.img,this.left, this.top, this.width, this.height)

        }else{

            img.onload=()=>{
    
                context.drawImage(this.img,this.left, this.top, this.width, this.height)
            
            }
        }


    }

}

// 画方形
class DrawRect extends BaseItem{

    constructor({top, left, width, height,speed, bg = '#333'}){

        super({top, left, width, height,speed})
        this.bg=bg
    }

    draw(context){

        context.fillStyle =this.bg

        context.fillRect(this.left,this.top,this.width,this.height)

    }
}

// 创建文字
class createText extends createElement{

    constructor({top,left,width,text,fontStyle,color}){

        super({top,left})
        this.text=text
        this.fontStyle=fontStyle
        this.color=color
        this.width = width
        
    }

    draw(ctx){

        var font = ctx.font = this.fontStyle;
        ctx.fillStyle = this.color;

        var text = this.text
        var subIndex = 0
        var lineWidth = 0
        var width = this.width
        var textArr = []
        var fontSize = Number(font.split("px")[0])
        var lineHeight = fontSize+5
        var top = this.top 

        // 有限定宽度
        if(width){

            for (let index = 0; index < text.length; index++) {
    
                let element = text[index];
                lineWidth += (ctx.measureText(element).width*fontSize/12)
    
                //console.log(ctx.measureText(element))
    
                if (lineWidth >= width) {
    
                    var subText = text.slice(subIndex, index)
                    subIndex = index
                    lineWidth = 0
    
                    //console.log(subText)
                    textArr.push({
                        subText,
                        top
                    })
    
                    top +=lineHeight
                }
    
            }

            // 最后一句话有点短，没有进入 数组
            if(lineWidth>0){

                textArr.push({
                    subText:text.slice(subIndex),
                    top
                })

            }

        }
        
        // 没有限定宽度
        else{
            
            textArr.push({
                subText:text,
                top
            })
        }

        // console.log('textArr',textArr);

        for (var i = 0; i < textArr.length; i++) {
            let element = textArr[i];
            ctx.fillText(element.subText, this.left, element.top);
        }

    }

}

    

/***
 * 
 *  实体类
 * 
 */

// 飞机类
class Plane extends DrawImage{

    constructor({top, left,
        width=gameConfig.planeSize[0],
        height=gameConfig.planeSize[1], 
        speed=gameConfig.planeSpeed,
        imgSrc=gameConfig.planeIcon}){

        super({top, left, width, height, speed ,imgSrc})

        this.control()
    }

    // 发射子弹
    fire(){

        var bullet = new Bullet({
            left: this.left + this.width / 2 - 2,
            top: this.top - 20,
        });

        // 更新子弹的位置 + 判断是否击中怪兽
        gameInfo.updateCallback.push(function() {
            
            bullet.fly()

            var monsters = gameInfo.monsters
            
            // 判断是否 击中怪兽
            for (var i=0,len = monsters.length;i< len;i++) {

                const monster = monsters[i];

                //  判断是否碰撞
                var isdetect = bullet.getIsDetect(monster);

                if (isdetect) {
                    
                    bullet.remove();
                    monster.eliminate(i)

                    updateScore(++gameInfo.score)

                    return;
                }
                
            }
        })

    }

    // 控制飞机
    control(){

        // 保存组合键
        var keyInfo={}

        //  方向键
        document.onkeydown =(e)=> {

            var index = e.keyCode;
            var left = this.left;
            var top = this.top;
            var w = this.width;
            var h = this.height;
            
            keyInfo[index] = true

            for (let eCode in keyInfo) {

                // left
                if (eCode == 37 && !(left <= 0)) {
                    this.move("left");
                } 
                
                // up
                else if (eCode == 38 && !(top <= 0)) {
                    
                    this.move("up");
                } 
                
                //right
                else if (eCode == 39 && !(left + w >= canvas.width)) {
                    
                    this.move("right");
                } 
                
                // down
                else if (eCode == 40 && !(top + h >= canvas.height)) {
                    
                    this.move("down");
                } 

                // 空格 发射
                else if (eCode == 32) {
                    
                    this.fire();
                }
                    
            }

            // e.preventDefault()

            // console.log('eCode',e);

        };

        // 删除组合键
        document.onkeyup=(e)=>{

            var eCode = e.keyCode;

            keyInfo[eCode] && (delete keyInfo[eCode])
            
            e.preventDefault()

        }
    }

}


// 子弹类
class Bullet extends DrawRect{
    
    constructor({top, left, 
        width=gameConfig.bulletSize[0], 
        height=gameConfig.bulletSize[1], 
        bg=gameConfig.bulletColor,
        speed=gameConfig.bulletSpeed }){

        super({top, left, width, height, bg ,speed})

    }

    // 子弹飞行
    fly(){

        // 如果 子弹飞出屏幕，就停止
        if (this.top < -2 * this.height || this.left < -2 * this.width) {
            this.remove({ });
            return;
        }

		this.move('up');

    }

}


// 怪兽类
class Monster extends DrawImage{
    
    constructor({top, left,
         width=gameConfig.enemySize[0], 
         height=gameConfig.enemySize[1],
         speed=gameConfig.enemySpeed[0],
         yspeed=gameConfig.enemySpeed[1],
         imgSrc=gameConfig.enemyIcon}){

        super({top, left, width, height,speed,imgSrc })

        // 纵向速度
        this.yspeed = yspeed

        gameInfo.monsters.push(this)
    }

    fly(direction,isDown){

        // 如果 怪兽飞出屏幕
        if (this.top+this.height >= canvas.height ) {
            gameInfo.gameState="lose"
            return;
        }

		this.move(direction);
        isDown && this.move("down",this.yspeed)

    }

    // 消除怪兽
    eliminate(i){

        gameInfo.monsters.splice(i,1)

        this.remove();

        // 展示爆炸图片
        var boom = new  DrawImage({
            top:this.top,
            left:this.left,
            height:this.height,
            width:this.width,
            imgSrc:gameConfig.enemyBoomIcon
        })

        setTimeout(function name(params) {
            boom.remove()
        },50)

    }

}

// 怪兽移动的方向
Monster.prototype.direction=gameConfig.enemyDirection


/***
 * 
 *  函数
 * 
 */


// 逐帧更新画布
function updateCanvas(params) {
    
    // 擦除画布函数，可以将整个画布擦除
    context.clearRect(0, 0, canvas.width, canvas.height);

    // 判断游戏是否结束
    if (isGameOver()) return 

    var updateCallback = gameInfo.updateCallback
    var len = updateCallback.length

    if(len==0) return 

    // 各种更新函数 = 判断是否碰边 + 位置更新
    for (var i = updateCallback.length; i--; ) {
        updateCallback[i]()
    }

    drawElements()

    gameInfo.stopAnimateCallback=requestAnimFrame(updateCanvas)

}


// 初始化页面元素容器
function initElements(score,state) {

    gameInfo.elements={}
    gameInfo.monsters=[]
    gameInfo.score=score
    gameInfo.gameState=state
    score==0 && (gameInfo.currentLevel= 1)

    // 把更新函数清空
    gameInfo.updateCallback=[]

    // 清除动画定时器
    cancelAnimFrame(gameInfo.stopAnimateCallback)
    
    // 设置个数属性不可遍历，主要为了给页面元素起一个名字
    Object.defineProperty(gameInfo.elements,"len",{
        enumerable:false,
        writable:true,
        value:0
    })
    
}


// 渲染页面保存的元素
function drawElements(){

    var elements = gameInfo.elements
    
    // 绘制所有元素
    for (var i in elements) {
        var item = elements[i]
		item.draw(context);
    }

}

// 游戏介绍
function gameStart(params) {

    initElements(0,"unstart")
    
    new DrawImage({
        top:190,
        left:380,
        height:249,
        width:232,
        imgSrc:"../img/bg.png"
    })

    new createText({
        top:180,
        left:55,
        text:"射击游戏 ",
        fontStyle:"30px Verdana,Arial, sans-serif",
        color:"#fff"
    })

    new createText({
        top:220,
        left:55,
        text:"这是一个令人欲罢不能的射击游戏，使用 ← 和 → 操作你的飞机，使用空格（space）进行射击。一起来消灭宇宙怪兽吧！",
        fontStyle:"14px arial",
        width:330,
        color:"#fff"
    })

    new createText({
        top:300,
        left:55,
        text:"当前Level："+gameInfo.currentLevel,
        fontStyle:"18px arial",
        color:"#fff"
    })

    new createText({
        top:350,
        left:55,
        text:"点击屏幕进入游戏！！",
        fontStyle:"24px arial",
        color:"#2DBE60"
    })
    
    drawElements()

}

// 游戏初始化关卡
function gameInit(level) {

    initElements(gameInfo.score,"gaming")
    
    // 创建一个飞机
    plane = new Plane({
        left: 50,
        top: 400,
        width: 60,
        height: 100
    })

    // 显示分数
    scoreText=new createText({
        top:40,
        left:20,
        text:"分数："+gameInfo.score,
        fontStyle:"18px  arial bold",
        color:"#fff"
    })
    
    // 创建怪兽
    createMoreMonster(6*gameInfo.currentLevel)

    // 更新画布
    updateCanvas()

}

// 判断是否游戏结束
function isGameOver() {
    
    var tip=""
    var score = 0
    var result = gameInfo.gameState
    var isWin = result =="win"
    var isLose = result=="lose"
    var optTip=""

    // 游戏输赢
    if(isWin || isLose) {

        var currentScore = gameInfo.score
        var currentLevel = gameInfo.currentLevel

        if(isWin){

            console.log('gameInfo.currentLevel',gameInfo.currentLevel,gameInfo.totalLevel);
            
            if(currentLevel==gameConfig.totalLevel){
                tip="恭喜通关了！！！"
                score=0
                result="over"
                optTip = "你已经玩完了，点击重新开始"
            }else{
                
                tip="你赢了"
                score = currentScore

                if(currentLevel==gameConfig.totalLevel-1){
                    optTip="点击屏幕进入 最后一关！"
                }else{
                    
                    optTip = "点击屏幕进入第 "+ (currentLevel+1)+" 关，（づ￣3￣）づ"
                }

            }

        }else if(isLose){
            
            tip="你输了"
            score=0
            optTip="点击屏幕重新开始啦"
        }
        
        initElements(score,result)

        new DrawImage({
            top:190,
            left:380,
            height:175,
            width:250,
            imgSrc:"../img/bg-end.png"
        })

        new createText({
            top:180,
            left:55,
            text:"游戏结束 "+tip,
            fontStyle:"30px arial",
            color:"#fff"
        })

        new createText({
            top:220,
            left:55,
            text:"最终得分："+currentScore,
            fontStyle:"20px arial",
            color:"#fff"
        })

        new createText({
            top:270,
            left:55,
            text:optTip,
            fontStyle:"20px arial",
            color:"#2DBE60"
        }) 

        drawElements()

        return true

    }

    return false

}

// 批量生产怪兽
function createMoreMonster(count){

    var numPerLine = gameConfig.numPerLine
    var row = parseInt(count/ numPerLine)
    
    var top  = 50
    var gap=gameConfig.planeSize[0]+gameConfig.enemyGap

    for (let index = 0; index < row; index++) {
        
        for (let i = 0; i < numPerLine; i++) {
            
            const monster = new Monster({
                left: 50+gap*i,
                top
            });

        }

        top+= gap

    }

    // 更新怪兽位置
    gameInfo.updateCallback.push(function(){

        var monsters = gameInfo.monsters

        // 已经没有怪兽了
        if(monsters.length==0) {
            
            gameInfo.gameState="win"
            return

        } 
        
        var isDown=false

        var a_monster =  monsters[0]

        var isOnLeft = a_monster.left<=0
        var isOnRight = monsters[monsters.length-1].left>= canvas.width-50
        
        // 判断是否碰到画布边缘，碰到就换方向运行
        if(isOnLeft ||isOnRight) {

            isOnLeft && (a_monster.direction="right")
            isOnRight && (a_monster.direction="left")
            isDown=true
        }
        
        var direction = a_monster.direction

        // 判断怪兽是否击中 飞机
        for (var i=0,len = monsters.length;i<len ;i++) {
            
            var monster = monsters[i]

            if(monster.getIsDetect(plane)){
                gameInfo.gameState="lose"
                return  
            }
            
            monster.fly(direction,isDown)

        }

    })
}

// 更新分数
function updateScore(score) {
    scoreText.text="分数："+score
}

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var plane,scoreText;

gameStart()

document.onclick=function (params) {

    var state = gameInfo.gameState

    // 游戏中无法继续
    if(state!=="gaming"){

        if(gameInfo.currentLevel<gameConfig.totalLevel){
            gameInfo.currentLevel++
        }

        gameInit(gameInfo.currentLevel)

    }

}

